package com.police.util;

import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;

/**
 * Created by LEO on 13-6-14.
 */
public final class Util {


    private static double EARTH_RADIUS = 6378.137;//地球半径
    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    static double pi = 3.14159265358979323;

    static double a = 6378245.0;
    static double ee = 0.00669342162296594323;

    public static class GPS {
        public double lon;
        public double lat;

        public GPS(double lon, double lat) {
            this.lat = lat;
            this.lon = lon;
        }
    }


    public static GPS transform(GPS w_point) {
        double wgLon = w_point.lon;
        double wgLat = w_point.lat;
        double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * pi;
        double magic = sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi);
        double mgLat = wgLat + dLat;
        double mgLon = wgLon + dLon;
        return bd_encrypt(mgLon, mgLat);
    }

    public static GPS formtrans(double ggLon, double ggLat) {
        double dLat = transformLat(ggLon - 105.0, ggLat - 35.0);
        double dLon = transformLon(ggLon - 105.0, ggLat - 35.0);
        double radLat = ggLat / 180.0 * pi;
        double magic = sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * cos(radLat) * pi);
        double wglat = ggLat - dLat;
        double wglon = ggLon - dLon;
        return new GPS(wglon, wglat);
    }

    static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * sqrt(Math.abs(x));
        ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * sin(y * pi) + 40.0 * sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * sin(y / 12.0 * pi) + 320 * sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * sqrt(Math.abs(x));
        ret += (20.0 * sin(6.0 * x * pi) + 20.0 * sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * sin(x * pi) + 40.0 * sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * sin(x / 12.0 * pi) + 300.0 * sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    public static GPS bd_encrypt(double gg_lon, double gg_lat) {
        double x = gg_lon, y = gg_lat;
        double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);
        double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);
        double bd_lon = (z * cos(theta) + 0.0065);
        double bd_lat = (z * sin(theta) + 0.006);
        return new GPS(bd_lon, bd_lat);
    }

    //    public static GeoPoint bd_encrypt(GeoPoint gg_point) {
//        double gg_lon = gg_point.getLongitudeE6(), gg_lat = gg_point.getLatitudeE6();
//        double x = gg_lon, y = gg_lat;
//        double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);
//        double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);
//        int bd_lon = (int) (z * cos(theta) + 0.0065);
//        int bd_lat = (int) (z * sin(theta) + 0.006);
//        return new GeoPoint(bd_lat, bd_lon);
//    }
//
//
    public static GPS bd_decrypt(double bd_lon, double bd_lat) {
        double x = bd_lon - 0.0065, y = bd_lat - 0.006;
        double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);
        double theta = atan2(y, x) - 0.000003 * cos(x * x_pi);
        double gg_lon = (z * cos(theta));
        double gg_lat = (z * sin(theta));
        return formtrans(gg_lon, gg_lat);
    }

    public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        return s;
    }


    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    public static double getDistance(GPS gps1, GPS gps2) {
        double radLat1 = rad(gps1.lat);
        double radLat2 = rad(gps2.lat);
        double a = radLat1 - radLat2;
        double b = rad(gps1.lon) - rad(gps2.lon);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        return s;
    }


    /**
     * 计算方位角nab，
     * 其中lat_a, lng_a是A的纬度和经度； lat_b, lng_b是B的纬度和经度。代码如下：
     */
    private double nab(double lat_a, double lng_a, double lat_b, double lng_b) {
        double d = 0.0;
        lat_a = lat_a * Math.PI / 180;
        lng_a = lng_a * Math.PI / 180;
        lat_b = lat_b * Math.PI / 180;
        lng_b = lng_b * Math.PI / 180;

        d = Math.sin(lat_a) * Math.sin(lat_b) + Math.cos(lat_a) * Math.cos(lat_b) * Math.cos(lng_b - lng_a);
        d = Math.sqrt(1 - d * d);
        d = Math.cos(lat_b) * Math.sin(lng_b - lng_a) / d;
        d = Math.asin(d) * 180 / Math.PI;

//     d = Math.round(d*10000);
        return d;
    }

    /**
     * 扇形点阵
     *
     * @param point   圆心
     * @param radius  半径
     * @param sDegree 开始度数
     * @param eDegree 结束度数
     * @return 扇形点阵
     */
    public static GPS[] sector(GPS point, double radius, double sDegree, double eDegree) {
        GPS[] points = new GPS[100];
        int step = ((int) ((eDegree - sDegree) / 10)) % 10;
        points[0] = point;
        int index = 1;
        double i = sDegree;
        while (i < eDegree + 0.001) {
            points[index++] = offset(point, radius, i);
            i += step;
        }
        return points;
    }


    private static GPS offset(GPS point, double dist, double bearing) {
        double latConv = getDistance(point, new GPS(point.lon + 0.1, point.lat)) * 10;
        double lngConv = getDistance(point, new GPS(point.lon, point.lat + 0.1)) * 10;
        double lat = dist * Math.cos(bearing * Math.PI / 180) / latConv;
        double lng = dist * Math.sin(bearing * Math.PI / 180) / lngConv;
        return new GPS(point.lon + lng, point.lat + lat);
    }

    public static void main(String... args) {
        double lon = 116.3913, lat = 39.9071;
        GPS geoPoint = new GPS(lon, lat);
        geoPoint = transform(geoPoint);
        System.out.println(geoPoint.lat + " , " + geoPoint.lon);
        //39.914845,116.403914

        //geoPoint.lon = 116.403914; geoPoint.lat = 39.914845;
        GPS point = bd_decrypt(geoPoint.lon, geoPoint.lat);
        System.out.println(point.lat + " , " + point.lon);
    }

}
